<?php

namespace App\Models\Train\TrainOrderPassengers\Repositories;

use App\Events\Train\TrainOrder\Refund\TicketReturnApplyEvent;
use App\Models\Train\TrainOrderPassengers\Repositories\Interfaces\TrainOrderPassengersRepositoryInterface;
use App\Models\Train\TrainOrderPassengers\TrainOrderPassenger;
use App\Models\Train\TrainOrders\Repositories\Interfaces\TrainOrdersRepositoryInterface;
use App\Models\Train\TrainOrders\TrainOrder;
use App\Models\Trip\RefundNeedPay\RefundNeedPay;
use App\Models\Trip\TripOrderCosts\Repositories\TripOrderPassengerCostsRepository;
use App\Models\Trip\TripOrderCosts\TripOrderCost;
use App\Models\Trip\TripOrderRefund\RefundDetail;
use App\Models\Trip\TripOrderRefund\TripOrderRefund;
use App\Notifications\Train\TrainOrder\TicketReturnFailedNotify;
use App\Notifications\Train\TrainOrder\TicketReturnSuccessNotify;
use App\Trip\Enums\OrderBusiness;
use App\Trip\Enums\TripTypes;
use App\Trip\Orders\ApplyRefund\TrainOrderApplyRefund;
use App\Trip\Orders\BookOrders\Train\SelectedSeats\MatchedPassengers;
use App\Trip\Orders\TripOrderState;
use App\Trip\Repository\BaseRepository;
use App\Trip\Train\Order\Interfaces\OrderPassengerRefundInterface;
use App\Trip\Train\Seat\Seats;
use Carbon\Carbon;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Str;
use Throwable;

class TrainOrderPassengersRepository extends BaseRepository implements TrainOrderPassengersRepositoryInterface
{
    public function __construct(TrainOrderPassenger $model)
    {
        parent::__construct($model);
    }

    public function addPassengers(TrainOrder $order, $data)
    {
        $passengers      = [];
        $passenger_costs = [];
        foreach ($data as $key => $passenger) {
            $sequence         = $passenger['sequence'] ?? 0;
            $sequence         = $sequence ?: $key + 1;
            $passengers[$key] = [
                'order_sn'                 => $order->getOrderSn(),
                'user_id'                  => $order->getUserId(),
                'passenger_id'             => $passenger['passenger_id'],
                'truename'                 => $passenger['truename'],
                'identity_type'            => $passenger['identity_type'],
                'identity_code'            => $passenger['identity_code'],
                'passenger_type'           => $passenger['passenger_type'],
                'mobile'                   => $passenger['mobile'],
                'seat_name'                => $passenger['seat_name'],
                'seat_name_uy'             => $passenger['seat_name_uy'],
                'carriage'                 => '',
                'seat_no'                  => '',
                'seat_price'               => $passenger['seat_price'],
                'ticket_price'             => $passenger['seat_price'],//默认选座时的价格,下单成功后更新
                'service_fee'              => $passenger['service_fee'],
                'order_state'              => $order->getOrderState(),
                'sequence'                 => $sequence,
                'origin_order_need_refund' => $passenger['origin_order_need_refund'] ?? 0,
                'change_ticket_loss'       => $passenger['change_ticket_loss'] ?? 0,
            ];

            foreach ($passenger['price_details'] as $price_detail) {
                $passenger_costs [] = [
                    "user_id"      => $order->getUserId(),
                    "trip_id"      => $order->getTripId(),
                    "trip_type"    => $order->getTripType(),
                    "order_sn"     => $order->getOrderSn(),
                    "passenger_id" => $passenger['passenger_id'],
                    "sequence"     => $sequence,
                    "type"         => $price_detail["type"],
                    "parent_type"  => $price_detail["type"],
                    "price"        => $price_detail["price"],
                    "name"         => $price_detail["name"],
                    "detail"       => $price_detail["detail"] ?? "",
                ];
            }
        }

        // passenger costs
        $passengerCostRepo = app(TripOrderPassengerCostsRepository::class);
        $passengerCostRepo->addMore($passenger_costs);
        return $this->model->insert($passengers);
    }

    public function addOnePassenger(TrainOrder $order, $data): TrainOrderPassenger
    {
        $data['user_id']  = $order->getUserId();
        $data['order_sn'] = $order->getOrderSn();
        return $this->create($data);
    }

    /**
     * 申请退票退款
     * 退票成功后 退款
     *
     * @param OrderPassengerRefundInterface $order_passenger_refund
     *
     * @return TripOrderRefund
     * @throws Exception|Throwable
     */
    public function refund(OrderPassengerRefundInterface $order_passenger_refund): TripOrderRefund
    {
        $passenger  = $order_passenger_refund->getPassenger();
        $order_info = $passenger->getOrderInfo();
        $apply      = new TrainOrderApplyRefund($order_info, $passenger);
        if ($order_passenger_refund->isIgnoreWorkTimeError()) {
            $apply->setIgnoreWorkTimeError();
        }

        if (!$apply->canApplyRefund()) {
            throw new Exception($apply->getErrorMsg(), $apply->getErrorCode());
        }

        $origin_order_sn = $order_info->getOriginOrderSn() ?: "";
        $need_pay_sn     = "";
        if ($order_passenger_refund->getShouldPayAmount() > 0) {
            // 验证是否支付了
            $need_pay      = new RefundNeedPay();
            $need_pay_info = $need_pay->findOneByTrainOrderPassenger($passenger);
            if (!$need_pay_info) {
                throw new Exception("请先支付需补交金额");
            } elseif (!$need_pay_info->isPaySuccess()) {
                throw new Exception("补交金额支付中,请稍后重试");
            }
            $need_pay_sn = $need_pay_info->getPaySn();
        }


        /**
         * 实际选座费
         * 先验证是否存在 选座费退款 如果存在跳过
         */
        $refund          = new TripOrderRefund();
        $select_seat_fee = $order_passenger_refund->getSelectedSeatAmount();
        $refund_amount   = $order_passenger_refund->getShouldRefundAmount();
        $order_pay_info  = $order_info->order_pay_info();
        $pre_refund_sn   = generate_order_id(TripTypes::TRAIN, OrderBusiness::REFUND);
        $refund_details  = [];
        $refund_data     = [
            "user_id"         => $passenger->getUserId(),
            "refund_sn"       => $pre_refund_sn,
            "out_refund_no"   => $pre_refund_sn,
            "out_trade_no"    => $order_info->getPaySn(),
            "origin_order_sn" => $origin_order_sn,
            "need_pay_sn"     => $need_pay_sn,
            "order_sn"        => $passenger->getOrderSn(),
            "ticket_price"    => $passenger->getTicketPrice(),
            "seat_no"         => $passenger->getSeatNo(),
            "refund_amount"   => $refund_amount,
            "pay_amount"      => $order_pay_info?->getPayAmount() ?: 0,
            "title"           => "退票退款",
            "refund_type"     => TripOrderRefund::REFUND_TYPE_USER_APPLY,
            "trip_type"       => $order_info->getTripType(),
        ];

        $refund_details[] = [
            "user_id"      => $passenger->getUserId(),
            "refund_sn"    => $pre_refund_sn,
            "order_sn"     => $passenger->getOrderSn(),
            "out_trade_no" => $order_info->getPaySn(),
            "passenger_id" => $passenger->getPassengerId(),
            "sequence"     => $passenger->getSequence(),
            "type"         => RefundDetail::RETURN_TICKET_OR_CHANGED_ORIGIN_ORDER_REFUND,//TripOrderCosts::COST_TYPE_TICKET,
            "name"         => "退票退款",
            "amount"       => $order_passenger_refund->ticketRefundAmount(),
            "trip_type"    => $order_info->getTripType(),
        ];

        // 验证是否退过选座费 或者存在退票记录
        if ($select_seat_fee > 0) {
            $refund_details[] = [
                "user_id"      => $passenger->getUserId(),
                "refund_sn"    => $pre_refund_sn,
                "order_sn"     => $passenger->getOrderSn(),
                "out_trade_no" => $order_info->getPaySn(),
                "passenger_id" => $passenger->getPassengerId(),
                "sequence"     => $passenger->getSequence(),
                "type"         => RefundDetail::SELECTED_SEATS_REFUND,
                "name"         => "优选铺席",
                "amount"       => $select_seat_fee,
                "trip_type"    => $order_info->getTripType(),
            ];
        }
        $refund_data['details'] = $refund_details;
        $refund_info            = $refund->applyOrderPassengerTicketReturnRefund($refund_data);
        $this->updatePassenger($passenger, [
            'refund_sn' => $refund_info->getRefundSn(),
        ]);
        /**
         * 退款申请成功后 发起 退票申请
         */
        event(new TicketReturnApplyEvent($passenger, $refund_info));
        return $refund_info;
    }

    public function updatePassenger(TrainOrderPassenger $passenger, array $data): bool
    {
        return $this->update($data, $passenger);
    }

    public function findOneOrderPassenger(string $order_sn, string $passenger_id, int $sequence): TrainOrderPassenger
    {
        return $this->findOneBy([
            'order_sn'     => $order_sn,
            'passenger_id' => $passenger_id,
            'sequence'     => $sequence,
        ]);
    }

    /**
     * 退票业务-单乘客-退票成功
     *
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     */
    public function ticketReturnSuccess(TrainOrderPassenger $passenger, TripOrderRefund $refund_info)
    {
        $order_info = $passenger->getOrderInfo();
        DB::transaction(function () use ($order_info, $passenger, $refund_info) {
            $refund_info->update([
                'refund_state' => TripOrderRefund::REFUND_STATE_REFUNDING,
                'refund_at'    => Carbon::now()->toDateTimeString(),
            ]);

            $orderRepo = app(TrainOrdersRepositoryInterface::class);
            $orderRepo->updateOrderState($order_info, [
                'order_state' => TripOrderState::ORDER_STATE_REFUNDED,
            ], collect([$passenger]));
        });
        try {
            $order_info->notify(new TicketReturnSuccessNotify($order_info, $refund_info));
        } catch (Exception $e) {
            Log::error("退票成功通知发送失败,{$order_info->getOrderSn()},R:{$refund_info->getRefundSn()},:{$e->getMessage()}");
        }
        $refund_reason = $this->passenger_apply_ticket_return_refund_title($passenger->getTrueName());
        $refund_info->refundNow($refund_reason);
    }

    /**
     * 乘客申请退票退款 标题
     * @param string $passenger_name
     * @return string
     */
    private function passenger_apply_ticket_return_refund_title(string $passenger_name = ""): string
    {
        return Str::replace(['{NAME}', '{Name}'], [$passenger_name, $passenger_name], __("refund/order_or_ticket_refund_title.乘客{NAME}申请退票"));
    }

    /**
     * 退票业务-单乘客-退票失败
     *
     * @param TrainOrderPassenger $passenger
     * @param TripOrderRefund     $refund_info
     * @param string              $failed_reason
     *
     * @return void
     * @throws Throwable
     */
    public function ticketReturnFailed(TrainOrderPassenger $passenger, TripOrderRefund $refund_info, string $failed_reason): void
    {
        $order_info = $passenger->getOrderInfo();
        if ($failed_reason == '乘客已退票') {
            Log::error("ticketReturnFailed:{$order_info->getOrderSn()}，异常：{$failed_reason}");
            return;
        }
        DB::transaction(function () use ($order_info, $passenger, $refund_info, $failed_reason) {
            $refund_info->update([
                'refund_state'  => TripOrderRefund::REFUND_STATE_TICKET_RETURN_FAILED,
                'failed_reason' => $failed_reason,
            ]);

            /**
             * @var TrainOrdersRepositoryInterface $orderRepo
             */
            $orderRepo = app(TrainOrdersRepositoryInterface::class);
            $orderRepo->updateOrderState($order_info, [
                'order_state' => TripOrderState::ORDER_STATE_REFUND_FAILED,
            ], collect([$passenger]));
        });

        $order_info->notify(new TicketReturnFailedNotify($order_info));
    }

    /**
     * 匹配到选座信息 并更新乘客成本 ~~
     *
     * @param MatchedPassengers $matchedPassenger
     *
     * @return void
     * @throws Throwable
     */
    public function setPassengerMatchedSeat(MatchedPassengers $matchedPassenger): void
    {
        $passenger   = $matchedPassenger->getPassenger();
        $matchedSeat = $matchedPassenger->getSeatInfo();
        $order       = $passenger->getOrderInfo();
        if ($passenger->isMatchedSeat()) {
            return;
        }
        DB::transaction(function () use ($order, $passenger, $matchedSeat) {
            $this->updatePassenger($passenger, [
                'matched_seat' => 1, // 标记乘客匹配了选座
            ]);
            $name           = $matchedSeat->getSeatType() == Seats::SEAT_TYPE_SEAT ? "优选座席" : "优选铺席";
            $passenger_cost = [
                "user_id"      => $order->getUserId(),
                "trip_id"      => $order->getTripId(),
                "trip_type"    => $order->getTripType(),
                "order_sn"     => $order->getOrderSn(),
                "passenger_id" => $passenger->getPassengerId(),
                "sequence"     => $passenger->getSequence(),
                "type"         => TripOrderCost::COST_TYPE_SELECT_SEAT,
                "parent_type"  => TripOrderCost::COST_TYPE_SELECT_SEAT,
                "price"        => $matchedSeat->getServiceFee(),
                "name"         => $name,
                "detail"       => $matchedSeat->getSeatName(),
                "pay_sn"       => $order->getPaySn(),
            ];

            // passenger costs
            $passengerCostRepo = app(TripOrderPassengerCostsRepository::class);
            $passengerCostRepo->addPassengerSelectSeatCost($passenger, $passenger_cost);
        });
        return;
    }
}